#pragma once
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;

template<typename T>
class DataReader
{
protected:
	static const int BUFSIZE = 1000;
	FILE *datafile, *metafile;
	char buf[BUFSIZE];
	bool parsed;
	T *tmpFrameBuf;
	int eleBytes;
public:
	int width, height;
	int curRecId, curRecClocks;


public:
	DataReader(char datafilename[], char metafilename[], int eleBytes = 1) {
		datafile = fopen(datafilename, "rb");
		if (datafile == NULL) {
			cout << "data file open failed." << endl;
		}

		metafile = fopen(metafilename, "r");
		if (metafile == NULL) {
			cout << "meta file open failed." << endl;
		}

		fgets(buf, BUFSIZE, metafile);
		fscanf(metafile, " %d %d\n", &width, &height);

		this->eleBytes = eleBytes;
		tmpFrameBuf = new T[eleBytes*width*height];

		parsed = false;
		curRecId = -1;
		curRecClocks = -1;
	}

	~DataReader(void) {
		fclose(metafile);
		fclose(datafile);
		delete[] tmpFrameBuf;
	}

	bool getNextRec(void) {
		if (parsed) getNextFrame(tmpFrameBuf);
		parsed = true;
		fgets(buf, BUFSIZE, metafile);
		if (buf[0] == '#') return false;
		sscanf(buf, " %d %d", &curRecId, &curRecClocks);
		return true;
	}

	virtual bool getNextFrame(T *sptr) {
		if (!parsed)
			if (getNextRec() == false) return false;
		parsed = false;
		fread(sptr, sizeof(T), eleBytes*width*height, datafile);
		return true;
	}
};

class DepthReader : public DataReader<short>{
public:
	DepthReader(char dfn[], char mfn[]) : DataReader(dfn, mfn){}
	DepthReader(void) : DataReader("depth_data.dat", "depth_meta.txt"){}
};
class GrayReader : public DataReader<unsigned char>{
public:
	GrayReader(char dfn[], char mfn[]) : DataReader(dfn, mfn){}
	GrayReader(void) : DataReader("gray_data.dat", "gray_meta.txt"){}
};

class ColorReader : public DataReader<unsigned char>{
public:
	ColorReader(char dfn[], char mfn[]) : DataReader(dfn, mfn, 3){}
	ColorReader(void) : DataReader("color_data.dat", "color_meta.txt", 3){}
};